// JORGE FERNANDO GOMEZ
// 1259371
// 189631
// LEC A1
// MARTIN MÜLLER
// D02
// YANG LIU


#line 10 "util.c"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include <curses.h>

#include "util.h"
#include "hash.h"
#include "gamestate.h"
#include "read.h"


/* streamList holds a list of open streams. */
static struct Streams * streamList = NULL;

/* Holds the the sketchpad stream *. */
static FILE ** sketchpad = NULL;
static FILE * logStream = NULL;

/* Dispatch table. */
//hash * dispatch = newHashTable(DEFAULT_SIZE);


/********************************************
 *                                          *
 *           UTILITY FUNCTIONS              *
 *                                          *
 ********************************************/

    /* ----------------------------------*/


/*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
 *                                                                           *
 *                              Management                                   *
 *                                                                           *
 *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*/

/* openStream() calls either fopen() or popen() with filename and mode
 * as arguments, depending on type given.  If it fails, it calls killim()
 * to terminate, or returns the pointer to the stream otherwise. */
FILE* openStream ( const char * filename, char type, const char * mode) {

    FILE * stream;

    if ( type == 'f' )
        stream = fopen(filename, mode);
    else if ( type == 'p' )
        stream = popen(filename, mode);
    else
        killim(__LINE__);

    if (stream == NULL)
        killim(__LINE__);

    return stream;
}

/* openLogStream opens a debugging stream to be stored in logStream. */
FILE* openLogStream ( const char * filename, char type, const char * mode) {
    FILE * stream;

    if ( type == 'f' )
        stream = fopen(filename, mode);
    else if ( type == 'p' )
        stream = popen(filename, mode);
    else
        killim(__LINE__);

    if (stream == NULL)
        killim(__LINE__);

    logStream = stream;
    return stream;
}

/* addStream() adds a stream to a list of streams streamList.
 * issketchpad is either 0 or 1 depending on wheter stream is
 * a pointer to a sketchpad or not. (0 - no, 1 - yes.) */
void addStream ( void * stream, char type, bool issketchpad ) {

    struct Streams * newstream = malloc(sizeof(struct Streams));

    if ( newstream == NULL )
        killim(__LINE__);

    /* Store the sketchpad pointer on our local variable. */
    if ( issketchpad )
        sketchpad = stream;

    newstream->stream_type = type;
    newstream->streamptr = stream;

    newstream->next = streamList;
    streamList = newstream;
}

/* getOutputStream() returns a pointer to the main output stream
 * (stored in 'sketchpad') or returns NULL if it doesn't exist. */
FILE** getOutputStream ( void ) {

    if ( sketchpad == NULL )
        return (FILE **) NULL;

    return sketchpad;
}

/* getLogStream() returns the pointer to the open debugging stream,
 * stored in logStream. */
FILE* getLogStream ( void ) {
    if ( logStream == NULL )
        return (FILE *) NULL;

    return logStream;
}

/* closeStreams() goes through a list of open streams
 * and calls either fclose() or pclose() depending on
 * stream type. */
void closeStreams ( void ) {

    /* Holds the current node temporarily to free it
     * after we've 'destroyed' stack stored there. */
    struct Streams * freeing;

    for ( ; streamList != NULL; ) {

        if ( streamList->stream_type == 'f' )
            fclose( *(streamList->streamptr) );

        else if ( streamList->stream_type == 'p' )
            pclose( *(streamList->streamptr) );

        freeing = streamList;
        streamList = streamList->next;

        free(freeing);
    }
}

/* killim() takes a line number, closes the program's open
 * streams, and frees allocated memory. It then prints an
 * error message with the line number numLine it was
 * called with and exits EXIT_FAILURE. */
void killim ( int numLine ) {

    /* Retrieves the allocated Lines array. */
    Line * Lines = getLines();

    if ( stdscr != NULL )
        endwin();

    /* Sends 'end' command to Sketchpad if it was open. */
    if ( sketchpad != NULL )
        fprintf(*sketchpad, "end\n");

    /* Close open streams. */
    closeStreams();
    free(Lines);

    /* Print an error to stderr with the line number
     * from where killim() was called. */
    fprintf(stderr, "lander: %d, error.\n", numLine);

    exit(EXIT_FAILURE);
}

    /* ----------------------------------*/

/*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
 *                                                                           *
 *                             General Tools                                 *
 *                                                                           *
 *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*/

/* getDoubles() skips blocks2skip blocks of non-whitespace
 * characters, and returns a double * doubles, which holds
 * two double numbers. */
double* getDoubles ( char * string, int blocks2skip, int count ) {

    /* Count holds the count of doubles we want to find. */
    if ( count == 0 )
        return (double *) 0;

    char * nextblock;       /* Pointer for strtod. */

    double * doubles = malloc( sizeof(double)*count );

    if ( doubles == NULL )
        killim(__LINE__);

    int start = 0;          /* Keeps track of position on string. */
    int blocksFound = 0;
    int incrementFlag = 1;

    while ( 1 ) {
        if ( isspace( *(string + start) )) {

            /* Stop when enough blocks have been skipped. */
            if ( blocksFound == blocks2skip )
                break;

            if ( !incrementFlag )
                incrementFlag++;

            start++;
        }
        if ( !isspace( *(string + start) )) {
            if ( incrementFlag ) {
                blocksFound++;
                incrementFlag--;
            }
            start++;
        }
    }

    /* Finally, get the 'double' parameters from our string. */
    *doubles = strtod( string+start, &nextblock );

    int i;
    for ( i = 1; i < count; i++ )
        *(doubles + i) = strtod( nextblock, &nextblock );

    return doubles;
}

/* getLongs() skips blocks2skip blocks of non-whitespace
 * characters, and returns a long * longs, which holds
 * long numbers. */
long* getLongs ( char * string, int blocks2skip, int count ) {

    /* Count holds the count of doubles we want to find. */
    if ( count == 0 )
        return (long *) 0;

    char * nextblock;       /* Pointer for strtod. */

    long * longs = malloc( sizeof(long)*2 );

    if ( longs == NULL )
        killim(__LINE__);

    int start = 0;          /* Keeps track of position on string. */
    int blocksFound = 0;
    int incrementFlag = 1;

    while ( 1 ) {
        if ( isspace( *(string + start) )) {

            /* Stop when enough blocks have been skipped. */
            if ( blocksFound == blocks2skip )
                break;

            if ( !incrementFlag )
                incrementFlag++;

            start++;
        }
        if ( !isspace( *(string + start) )) {
            if ( incrementFlag ) {
                blocksFound++;
                incrementFlag--;
            }
            start++;
        }
    }

    /* Finally, get the 'double' parameters from our string. */
    *longs = strtod( string+start, &nextblock );

    int i;
    for ( i = 1; i < count; i++ )
        *(longs + i) = strtod( nextblock, &nextblock );

    return longs;
}

/* substr() takes string and creates a new string containig the characters
 * of string from index offset to index length.
 * Returns the resulting substring or the NULL pointer if string is smaller
 * than the provided offset + length */
char* substr (const char * string, int offset, int length) {

    int strLength = strlen(string);
    char * substring = malloc( sizeof(char)*(length+1) );

    if ( substring == NULL )
        killim(__LINE__);

    if ( strLength < (offset + length) )
        return(NULL);

    int i, j;
    for ( i = offset, j = 0; i < (offset+length); i++, j++) {
        *(substring + j) = *(string + i);
    }

    *(substring + length) = '\0';
    return substring;
}

/* chompim() takes lineString as argument and removes
 * any trailing whitespace from the string */
void chompim (char * lineString) {
    int len = strlen(lineString);
    int offset = 1;

    while ( isspace( *(lineString + len - offset) ))
        offset++;

    *(lineString+len-offset+1) = '\0';
}

/* Exp() exponentiates some base with exponent. */
long Exp ( int base, int exponent ) {
    int current_base = base;
    int count = 1;

    while ( count < exponent ) {
        current_base = current_base * base;
        count++;
    }
    return current_base;
}
